package org.protege.owl.examples.tab;

import java.util.*;
import java.net.URI;

import org.semanticweb.owl.inference.OWLReasoner;
import org.semanticweb.owl.inference.OWLReasonerFactory;
import org.semanticweb.owl.model.*;
import org.semanticweb.owl.util.*;
import uk.ac.manchester.cs.factplusplus.protege.*;
import org.protege.editor.owl.model.OWLModelManager;
import javax.swing.JTextArea;

public class PredicateRegistry
{	
	private Map<String,Predicate> predicates;
	private Map<String,InstanceSet> instances;
	
	private Set<OWLClass> classes;
	private Set<OWLObjectProperty> objectProperties;
	private Set<OWLIndividual> individuals;
	
	private OWLModelManager model;
	private OWLOntology ontology;
	
	private VariableRegistry variables;
	
	public PredicateRegistry (OWLModelManager mod)
	{
		model = mod;
		ontology = model.getActiveOntology();
		
		
		OWLReasonerFactory reasonerFactory = new FaCTPlusPlusReasonerFactory();
		OWLOntologyManager ontMan = model.getOWLOntologyManager();
		OWLReasoner reasoner = reasonerFactory.createReasoner(ontMan);
		try
		{
			reasoner.loadOntologies(Collections.singleton(ontology));
			reasoner.classify();
		
			List<InferredAxiomGenerator<? extends OWLAxiom>> gens = new ArrayList<InferredAxiomGenerator<? extends OWLAxiom>>();
			// Add all inferred axioms generators
			gens.add(new InferredDisjointClassesAxiomGenerator());
			gens.add(new InferredEquivalentClassAxiomGenerator());
			gens.add(new InferredSubClassAxiomGenerator());
			gens.add(new InferredDataPropertyCharacteristicAxiomGenerator());
			gens.add(new InferredEquivalentDataPropertiesAxiomGenerator());
			gens.add(new InferredSubDataPropertyAxiomGenerator());
			gens.add(new InferredClassAssertionAxiomGenerator());
			gens.add(new InferredPropertyAssertionGenerator());
			gens.add(new InferredEquivalentObjectPropertyAxiomGenerator());
			gens.add(new InferredInverseObjectPropertiesAxiomGenerator());
			gens.add(new InferredSubObjectPropertyAxiomGenerator());
			
			//Uruchomienie powoduje poteznego buga
			//gens.add(new InferredObjectPropertyCharacteristicAxiomGenerator());
	
			URI ontologyURI = URI.create("http://www.elka.pw.edu.pl/follow.owl");
			URI physicalURI = URI.create("file:///D:/tmp/follow.owl");
			SimpleURIMapper mapper = new SimpleURIMapper(ontologyURI,physicalURI);
			ontMan.addURIMapper(mapper);
			OWLOntology infOnt = ontMan.createOntology(ontologyURI);
			
			InferredOntologyGenerator iog = new InferredOntologyGenerator(reasoner, gens);
			iog.fillOntology(ontMan, infOnt);
			
			ontMan.saveOntology(infOnt);
			 
			model.setActiveOntology(infOnt);
			ontology = infOnt;
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		classes = ontology.getClassesInSignature();
		objectProperties = ontology.getObjectPropertiesInSignature();
		individuals = ontology.getIndividualsInSignature();
		
		predicates = new HashMap<String,Predicate>();
		instances = new HashMap<String,InstanceSet>();
		
		Iterator<OWLClass> itC = classes.iterator();
		Iterator<OWLObjectProperty> itP = objectProperties.iterator();

		while (itC.hasNext())
		{
			String newPredicateName = itC.next().toString();
			predicates.put(newPredicateName,new Predicate(newPredicateName,1));
			predicates.put("NOT "+newPredicateName,new Predicate(newPredicateName,1,false));
		}
		while (itP.hasNext())
		{
			String newPredicateName = itP.next().toString();
			predicates.put(newPredicateName,new Predicate(newPredicateName,2));
			predicates.put("NOT "+ newPredicateName, new Predicate(newPredicateName,2,false));
		}
		//Collections.sort(predicates, new AlphabeticComparator());
	}
	
	public void setVariableRegistry(VariableRegistry varReg)
	{
		variables = varReg;
	}
	
	public boolean check (String predName, int args)
	{
		if (predicates.keySet().contains(predName))
		{
			int storedArgNmb=predicates.get(predName).argumentsNmb();
			if (storedArgNmb==args || storedArgNmb==0)
				return true;
			else
				return false;
		}
		else 
			return false;
	}
	
	public boolean check (String pred)
	{
		return check(pred,0);
	}
	
	public boolean check (Predicate pred)
	{
		return predicates.keySet().contains(pred);
	}
	
	public InstanceSet getInstances(String predName) throws InappropriateInstanceSetUsage, NonexistentEntityException
	{
		if (instances.containsKey(predName))
			return instances.get(predName);
		else
		{
			Predicate pred = predicates.get(predName);
			if (pred==null) throw new NonexistentEntityException();
			InstanceSet newInstancesSet = new InstanceSet(pred);
			//model.getActiveOntology().getClassAssertionAxioms(model.getOWLClass(predName)).iterator().next().getIndividual();
			
			if(pred.argumentsNmb()==1)
				if (pred.positive())
		            for (OWLClassAssertionAxiom ax : ontology.getClassAssertionAxioms(model.getOWLClass(pred.name()))) {
		                newInstancesSet.add(ax.getIndividual().toString());
		            }
				else
				{
					Set<String> positiveSet = new HashSet<String>();
					for (OWLClassAssertionAxiom ax : ontology.getClassAssertionAxioms(model.getOWLClass(pred.name()))) {
		                positiveSet.add(ax.getIndividual().toString());
		            }
					Iterator<OWLIndividual> indIt = individuals.iterator();
					while(indIt.hasNext())
					{
						OWLIndividual currInd = indIt.next();
						String currIndName = currInd.toString();
						if(!positiveSet.contains(currIndName))
							newInstancesSet.add(currIndName);	
					}
				}
			
			if(pred.argumentsNmb()==2)
			{
				Iterator<OWLIndividual> indIt = individuals.iterator();
	
				if(pred.positive())
				{
					while (indIt.hasNext())
					{
						OWLIndividual currInd = indIt.next();
						Iterator<OWLObjectPropertyAssertionAxiom> propAxiomsIt = ontology.getObjectPropertyAssertionAxioms(currInd).iterator();
						while(propAxiomsIt.hasNext())
						{
							OWLObjectPropertyAssertionAxiom currProp = propAxiomsIt.next();
							if (currProp.getProperty().toString().equals(pred.name()))
							{
								newInstancesSet.add(currInd.toString(), currProp.getObject().toString());
							}
						}
					}
				}
				else
				{
					while (indIt.hasNext())
					{
						Set<String> positiveSet = new HashSet<String>();
						OWLIndividual currInd = indIt.next();
						Iterator<OWLObjectPropertyAssertionAxiom> propAxiomsIt = ontology.getObjectPropertyAssertionAxioms(currInd).iterator();
						while(propAxiomsIt.hasNext())
						{
							OWLObjectPropertyAssertionAxiom currProp = propAxiomsIt.next();
							if (currProp.getProperty().toString().equals(pred.name()))
							{
								positiveSet.add(currProp.getObject().toString());
							}
						}
						Iterator<OWLIndividual> localIndIt = individuals.iterator();
						while(localIndIt.hasNext())
						{
							OWLIndividual currLocalInd = localIndIt.next();
							String currLocalIndName = currLocalInd.toString();
							if(!positiveSet.contains(currLocalIndName))
							{
								newInstancesSet.add(currInd.toString(),currLocalIndName);
								//System.out.println(currLocalIndName);
							}
						}
					}
				}
			}
            instances.put(predName, newInstancesSet);
            return newInstancesSet;
		}	
	}
	
	
	public void listPredicates(JTextArea textarea)
	{
		Iterator<String> it = predicates.keySet().iterator();
		textarea.setText(textarea.getText()+"\n\n\nAll predicates:\n");
		int i=0;
		while (it.hasNext())
		{
			i++;
			textarea.setText(textarea.getText()+"\n"+i+": "+it.next());
		}
	}
	
	
	public ResultTable getArgResultTable(String arg)
	{
		ResultTable newResultTable = new ResultTable(); 
		/*if (variables.check(arg))
		{
			Variable currVar = variables.getVariable(arg);
			newResultTable.addColumn(currVar);
			newResultTable.addInstance(currVar, ResultTable.DONTCARE);
			return newResultTable;
		}
		else
		{*/
			Iterator<OWLIndividual> indIt = individuals.iterator();
			while(indIt.hasNext())
			{
				if (indIt.next().toString().equals(arg))
				{
					Variable currVar = new Variable("?"+arg);
					newResultTable.addColumn(currVar);
					newResultTable.addInstance(currVar, arg);
					return newResultTable;
				}
			}
			if (variables.check(arg))
			{
				Variable currVar = variables.getVariable(arg);
				newResultTable.addColumn(currVar);
				newResultTable.addInstance(currVar, ResultTable.DONTCARE);
				return newResultTable;
			}
			else
			{
				Variable newVar = new Variable(arg);
				variables.add(newVar);
				newResultTable.addColumn(newVar);
				newResultTable.addInstance(newVar, ResultTable.DONTCARE);
				return newResultTable;
			}
			
			
			//throw new NonexistentEntityException();
		//}	
	}
	
	public ResultTable getFunctionResultTable(String funcName, ResultTable arg) throws NonexistentEntityException, NotUniformSizeException, InappropriateInstanceSetUsage
	{
		ResultTable newResultTable = new ResultTable();
		Variable currVar = arg.getVarialbeSet().iterator().next();
		Variable newVariable = new Variable(funcName + " OF " + currVar.name());
		newResultTable.addColumn(newVariable);
		if (predicates.containsKey(funcName))
		{
			InstanceSet currInstSet = getInstances(funcName);
			for (int i=0;i<arg.size(); i++)
			{
				String currArg = arg.getInstance(currVar, i);
				for(int j=0;j<currInstSet.size();j++)
				{
					if(currArg.equals(currInstSet.getColumn(1).get(j)))
					{
						newResultTable.addInstance(newVariable, currInstSet.getColumn(2).get(j));
					}
				}
			}	
			return newResultTable;
		}
		throw new NonexistentEntityException();
	}
	
	public ResultTable getResults(String predName, ResultTable arg1, ResultTable arg2) throws Exception
	{
		Predicate pred = predicates.get(predName);
		InstanceSet currInstSet = getInstances(predName);
		ResultTable initialResultTable =new ResultTable();
		Variable arg1var = arg1.getVarialbeSet().iterator().next();
		Variable arg2var; 
		if (pred.argumentsNmb()==2)
			arg2var =  arg2.getVarialbeSet().iterator().next();
		else 
			arg2var = null;
		initialResultTable.addColumn(arg1var,currInstSet.getColumn(1));
		if(pred.argumentsNmb()==2)
			initialResultTable.addColumn(arg2var,currInstSet.getColumn(2));
		ResultTable newResultTable = ResultTable.and(initialResultTable,arg1);
		if(pred.argumentsNmb()==2)
			newResultTable = ResultTable.and(newResultTable,arg2);
		return newResultTable;
	}
	
	public ResultTable getResults(String predName, ResultTable arg1) throws Exception
	{
		return getResults(predName,arg1,null);
	}
	
	
	/**
	 * @deprecated
	 * @param predName
	 * @param arg1
	 * @param arg2
	 * @return
	 * @throws InappropriateInstanceSetUsage
	 * @throws NonexistentEntityException 
	 */
	public ResultTable getResultTable(String predName, String arg1, String arg2) throws InappropriateInstanceSetUsage, NonexistentEntityException
	{
		ResultTable result = new ResultTable();
		Predicate pred = predicates.get(predName);
		InstanceSet inst = getInstances(predName);
		if(variables.check(arg1))
		{
			Variable var1 = variables.getVariable(arg1);
			if(pred.argumentsNmb()==1)
				result.addColumn(var1, inst.getColumn(1));
			if(pred.argumentsNmb()==2)
				if(variables.check(arg2))
				{
					result.addColumn(var1, inst.getColumn(1));
					result.addColumn(variables.getVariable(arg2), inst.getColumn(2));
				}	
				else
				{
					result.addColumn(var1);
					Variable var2 = new Variable("?"+arg2,true);
					result.addColumn(var2);
					
					for(int i=0;i<inst.size();i++)	
					{
						String currInst = inst.getColumn(2).get(i);
						if(currInst.equals(arg2))
						{
							result.addInstance(var1,inst.getColumn(1).get(i));
							result.addInstance(var2,currInst);
						}
					}				
				}
		}
		else
		{
			Variable var1 = new Variable("?"+arg1,true);
			result.addColumn(var1);
			if(pred.argumentsNmb()==1)
				for(int i=0; i<inst.size(); i++)
				{
					String currInst = inst.getColumn(1).get(i);
					if(currInst.equals(arg1))
						result.addInstance(var1, currInst);
				}
			if(pred.argumentsNmb()==2)
				if(variables.check(arg2))
				{
					Variable var2 = variables.getVariable(arg2);
					result.addColumn(var2);
					for(int i=0;i<inst.size();i++)	
					{
						String currInst = inst.getColumn(1).get(i);
						if(currInst.equals(arg1))
						{
							result.addInstance(var1,currInst);
							result.addInstance(var2,inst.getColumn(2).get(i));
						}
					}		
				}
				else
				{
					Variable var2 = new Variable("?"+arg2,true);
					result.addColumn(var2);
					for(int i=0; i<inst.size(); i++)
					{
						String currInst1 = inst.getColumn(1).get(i);
						String currInst2 = inst.getColumn(2).get(i);
						if(currInst1.equals(arg1) && currInst2.equals(arg2))
						{
							result.addInstance(var1, currInst1);
							result.addInstance(var2, currInst2);
						}
					}
				}
			
		}
		return result;
	}
	/**
	 * @deprecated
	 * @param predName
	 * @param arg1
	 * @return
	 * @throws InappropriateInstanceSetUsage
	 * @throws NonexistentEntityException
	 */
	public ResultTable getResultTable(String predName, String arg1) throws InappropriateInstanceSetUsage, NonexistentEntityException
	{
		return getResultTable(predName, arg1, null);
	}
	
}